package net.oktaeder.office.simulation;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import net.oktaeder.office.MainApplication;
import net.oktaeder.office.model.Time;
import net.oktaeder.office.model.EmployeeCollection;
import net.oktaeder.office.model.WorkOrder;
import net.oktaeder.office.utils.generators.EmployeeGenerator;
import net.oktaeder.office.view.MailDaemon;

/**
 * Main class for simulation and the entry point into simulation sub-system
 * 
 * @author tuukka
 *
 */
public class Simulation {

	private static Simulation instance;
	private static Log log = LogFactory.getLog(Simulation.class);
	private ArrayList<WorkOrder> orders;
	private Time time;
	private EmployeeCollection workForce;
	private LabourMarket labourMarket;
	private MailDaemon mailDaemon;
		
	private Simulation() throws Exception {
		try {
			orders = new ArrayList<WorkOrder>();
			time = new Time();
			initializeTime();
			workForce = new EmployeeCollection();
			labourMarket = new LabourMarket();
			mailDaemon = new MailDaemon();			
		} catch (Exception ex) {
			log.fatal("Failed to initialize simulation", ex);
			throw new Exception("Failed to initialize simulation", ex);
		}
	}
	
	public synchronized static Simulation getInstance() {
		try {
			if (instance == null) {
				instance = new Simulation();
			}
			return instance;
		} catch (Exception ex) {
			log.fatal("Failed to get instance of Simulation", ex);
			return null;
		}
	}
	
	public void receiveSystemMail(WorkOrder order) {
		log.info("Mail received");
		this.orders.add(order);
	}
	
	public void handleSystemMails() {
		//TODO: implement properly
		Iterator ordersIterator;
		WorkOrder order;
		
		ordersIterator = orders.iterator();
		
		while (ordersIterator.hasNext()) {
			//TODO: see if you can get rid of the type cast
			order = (WorkOrder)ordersIterator.next();
			if (order.getValue("action").equals("quit")) {
				log.info("Quit message received, signaling shutdown");
				MainApplication.getInstance().signalShutDown();
			}
		}
	}
	
	/**
	 * Retrieve Mail Daemon used to pass emails around
	 * 
	 * @return MailDaemon
	 */
	public MailDaemon getMailDaemon() {
		return this.mailDaemon;
	}
	
	public int amountOfSystemOrders() {
		return orders.size();
	}
	
	public Time getTime() {
		return time;
	}
	
	/**
	 * temporary method to initialize work force pool, until
	 * a centralized method is written
	 *
	 */
	public void initializeWorkForce() {
		Hashtable<String, Object> parameters;
		EmployeeGenerator generator;
					
		try {
			log.info("Initializing work force");
			
			parameters = new Hashtable<String, Object>();
			parameters.put("age", new Integer(25));
			generator = EmployeeGenerator.getInstance();
			
			//TODO: move this into a generator
			workForce.addEmployee(generator.generateEmployee(parameters));
			workForce.addEmployee(generator.generateEmployee(parameters));
			workForce.addEmployee(generator.generateEmployee(parameters));
			workForce.addEmployee(generator.generateEmployee(parameters));
			workForce.addEmployee(generator.generateEmployee(parameters));
			
			parameters = new Hashtable<String, Object>();
			parameters.put("age", new Integer(35));
			
			workForce.addEmployee(generator.generateEmployee(parameters));
			workForce.addEmployee(generator.generateEmployee(parameters));
			workForce.addEmployee(generator.generateEmployee(parameters));

			parameters = new Hashtable<String, Object>();
			parameters.put("age", new Integer(45));
			
			workForce.addEmployee(generator.generateEmployee(parameters));
			
			log.info("work force initialized");
			
		} catch (Exception ex) {
			log.error("Failed to initialize work force", ex);
		}
	}
	
	/**
	 * Attach workforce into Simulation
	 * 
	 * @param collection Workforce to use
	 */
	void setWorkForce(EmployeeCollection collection) {
		this.workForce = collection;
	}
	
	/**
	 * Get workforce currently used in simulation
	 * 
	 * @return Workforce
	 */
	EmployeeCollection getWorkForce() {
		return this.workForce;
	}
	
	/**
	 * temporary method to initialize time, until
	 * a centralized method is written
	 *
	 */
	private void initializeTime() {
		time.setMonth(1);
		time.setWeek(1);
		time.setYear(2010);		
	}
	
	public LabourMarket getLabourMarket() {
		return labourMarket;
	}
}